package com.coderly.onlinegallerysharingplatform.common.auth;

import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.json.JSONUtil;
import com.coderly.onlinegallerysharingplatform.common.convention.errorcode.ErrorCode;
import com.coderly.onlinegallerysharingplatform.enums.SpaceRoleEnum;
import com.coderly.onlinegallerysharingplatform.enums.SpaceTypeEnum;
import com.coderly.onlinegallerysharingplatform.model.dao.entity.*;
import com.coderly.onlinegallerysharingplatform.model.dao.mapper.UserMapper;
import com.coderly.onlinegallerysharingplatform.service.*;
import com.coderly.onlinegallerysharingplatform.toolkit.ExceptionTrowUtils;
import com.coderly.onlinegallerysharingplatform.toolkit.StpKit;
import com.coderly.onlinegallerysharingplatform.toolkit.UserUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

@Component// 保证此类被 SpringBoot 扫描，完成 Sa-Token 的自定义权限验证扩展
@Slf4j
public class StpInterfaceImpl implements StpInterface {

    @Resource
    private UserMapper userMapper;
    @Resource
    private SpaceService spaceService;
    @Resource
    private SpaceUserService spaceUserService;
    @Resource
    private SpaceRolePermService spaceRolePermService;
    @Resource
    private PictureService pictureService;
    @Value("${server.servlet.context-path}")
    private String contextPath;


    /**
     * 返回一个账户所拥有的权限
     * 核心在于通过各种请求参数获得到space_user表中的space_role角色，然后通过角色获取到权限列表
     * 权限判断逻辑：
     * 1. 基础校验：检查登录状态和会话类型
     * 2. 上下文判断：获取请求上下文，判断访问类型
     * 3. 权限获取：根据不同场景（公共库、私有空间、团队空间）获取对应权限
     *
     * @param loginId   用户id
     * @param loginType 会话类型，本项目space会话采用此鉴权
     * @return 权限列表
     */

    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 基础校验
        if (!isValidLoginContext(loginId, loginType)) {
            return List.of();
        }

        // 获取管理员权限和上下文
        List<String> adminPermissions = getSpacePermissionsBySpaceRole(SpaceRoleEnum.ADMIN.getValue());
        SpaceUserAuthContext authContext = getAuthContextByRequest();

        // 处理公共库访问
        if (isAllFieldsNull(authContext)) {
            return adminPermissions;
        }

        // 获取并校验登录用户ID
        Long userId = StpKit.SPACE.getLoginIdAsLong();
        ExceptionTrowUtils.throwIf(userId == null, ErrorCode.NOT_LOGIN_ERROR);

        // 处理直接包含SpaceUser的情况
        if (authContext.getSpaceUser() != null) {
            return getSpacePermissionsBySpaceRole(authContext.getSpaceUser().getSpaceRole());
        }

        // 处理SpaceUserId的情况
        List<String> spaceUserPermissions = handleSpaceUserIdPermissions(authContext, userId);
        if (spaceUserPermissions != null) {
            return spaceUserPermissions;
        }

        // 处理SpaceId或PictureId的情况
        return handleSpaceOrPicturePermissions(authContext, userId, adminPermissions);
    }

    /**
     * 校验登录上下文是否有效
     */
    private boolean isValidLoginContext(Object loginId, String loginType) {
        return loginId != null && Objects.equals(loginType, StpKit.SPACE.loginType);
    }

    /**
     * 处理基于SpaceUserId的权限获取
     */
    private List<String> handleSpaceUserIdPermissions(SpaceUserAuthContext authContext, Long userId) {
        Long spaceUserId = authContext.getSpaceUserId();
        if (spaceUserId == null) {
            return null;
        }

        SpaceUserDO spaceUser = spaceUserService.getById(spaceUserId);
        ExceptionTrowUtils.throwIf(spaceUser == null, ErrorCode.NOT_FOUND_ERROR, "未找到空间用户信息");

        SpaceUserDO loginSpaceUser = spaceUserService.lambdaQuery()
                .eq(SpaceUserDO::getSpaceId, spaceUser.getSpaceId())
                .eq(SpaceUserDO::getUserId, userId)
                .one();

        return loginSpaceUser == null ? List.of() : 
               getSpacePermissionsBySpaceRole(loginSpaceUser.getSpaceRole());
    }

    /**
     * 处理基于Space或Picture的权限获取
     */
    private List<String> handleSpaceOrPicturePermissions(SpaceUserAuthContext authContext, 
                                                        Long userId, 
                                                        List<String> adminPermissions) {
        // 获取spaceId，可能为null
        Long spaceId = getEffectiveSpaceId(authContext, userId, adminPermissions);
        if (spaceId == null) {
            return adminPermissions;
        }

        // 获取并验证空间信息
        SpaceDO space = spaceService.getById(spaceId);
        ExceptionTrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "未找到空间信息");

        return getSpaceTypeBasedPermissions(space, userId, adminPermissions);
    }

    /**
     * 获取有效的空间ID
     */
    private Long getEffectiveSpaceId(SpaceUserAuthContext authContext, 
                                    Long userId, 
                                    List<String> adminPermissions) {
        Long spaceId = authContext.getSpaceId();
        if (spaceId != null) {
            return spaceId;
        }

        Long pictureId = authContext.getPictureId();
        if (pictureId == null) {
            return null;
        }

        PictureDO picture = pictureService.lambdaQuery()
                .eq(PictureDO::getId, pictureId)
                .select(PictureDO::getId, PictureDO::getSpaceId, PictureDO::getUserId)
                .one();
        ExceptionTrowUtils.throwIf(picture == null, ErrorCode.NOT_FOUND_ERROR, "未找到图片信息");

        return picture.getSpaceId();
    }

    /**
     * 根据空间类型获取权限
     */
    private List<String> getSpaceTypeBasedPermissions(SpaceDO space, 
                                                     Long userId, 
                                                     List<String> adminPermissions) {
        if (space.getSpaceType() == SpaceTypeEnum.PRIVATE.getValue()) {
            return handlePrivateSpacePermissions(space, userId, adminPermissions);
        } else {
            return handleTeamSpacePermissions(space, userId);
        }
    }

    /**
     * 处理私有空间的权限
     */
    private List<String> handlePrivateSpacePermissions(SpaceDO space, 
                                                      Long userId, 
                                                      List<String> adminPermissions) {
        return (space.getUserId().equals(userId) || UserUtil.isAdmin()) ? 
               adminPermissions : new ArrayList<>();
    }

    /**
     * 处理团队空间的权限
     */
    private List<String> handleTeamSpacePermissions(SpaceDO space, Long userId) {
        SpaceUserDO spaceUser = spaceUserService.lambdaQuery()
                .eq(SpaceUserDO::getSpaceId, space.getId())
                .eq(SpaceUserDO::getUserId, userId)
                .one();
        return spaceUser == null ? new ArrayList<>() : 
               getSpacePermissionsBySpaceRole(spaceUser.getSpaceRole());
    }

        /**
         *
         * @param loginId 通常表示当前登录用户的唯一标识符（例如用户 ID）。
         * @param loginType 通常表示当前登录的会话标识符（例如会话 ID），但在角色列表的获取中，这个参数通常用不到。
         * @return
         */
        // 必须是在登录状态下，才会校验角色
        @Override
        public List<String> getRoleList (Object loginId, String loginType){
            if (loginId == null) {
                return List.of();
            }
            if (Objects.equals(loginType, StpKit.DEFAULT.loginType)) {
                log.info("获取默认登录类型的角色列表");
                Long userId = Long.parseLong(loginId.toString());
                UserDO user = userMapper.selectById(userId);
                if (user == null || user.getRole() == null) {
                    return List.of();
                }
                ArrayList<String> roles = new ArrayList<>();
                roles.add(user.getRole());
                return roles;
            }
            if (Objects.equals(loginType, StpKit.SPACE_TYPE)) {
                log.info("获取团队空间登录类型的角色列表");
                ArrayList<String> roles = new ArrayList<>();
                roles.add("viewer");
                return roles;
            }
            return null;
        }


        /**
         * 从请求中获取上下文对象
         */
        private SpaceUserAuthContext getAuthContextByRequest () {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
            String contentType = request.getHeader(Header.CONTENT_TYPE.getValue());
            SpaceUserAuthContext authRequest;
            // 兼容 get 和 post 操作，获取请求参数，存入SpaceUserAuthContext中
            if (ContentType.JSON.getValue().equals(contentType)) {
                String body = ServletUtil.getBody(request); // 读取的body是一个流，而流只能读一次，需要特殊处理
                authRequest = JSONUtil.toBean(body, SpaceUserAuthContext.class);
            } else {
                Map<String, String> paramMap = ServletUtil.getParamMap(request);
                authRequest = BeanUtil.toBean(paramMap, SpaceUserAuthContext.class);
            }
            // 根据请求路径区分 id 字段的含义
            Long id = authRequest.getId();
            if (ObjUtil.isNotNull(id)) {
                // 获取请求URI：/api/picture or /api/admin/picture
                String requestUri = request.getRequestURI();
                // 替换上下文 /api 剩下/picture or /admin/picture
                String partUri = requestUri.replace(contextPath + "/", "");
                // 获取第一个前缀 picture or admin/picture
                String moduleName = StrUtil.subBefore(partUri, "/", false);
                // 如果是/admin开头，还需要剥离一层
                //TODO admin开头的无需鉴权，此处需要调整
                if ("admin".equals(moduleName)) {
                    // 获取admin之后的部分
                    String afterAdmin = StrUtil.subAfter(partUri, "admin/", false);
                    // 再获取第一个/前的部分作为真正的moduleName
                    moduleName = StrUtil.subBefore(afterAdmin, "/", false);
                }
                switch (moduleName) {
                    case "picture":
                        authRequest.setPictureId(id);
                        break;
                    case "spaceUser":
                        authRequest.setSpaceUserId(id);
                        break;
                    case "space":
                        authRequest.setSpaceId(id);
                        break;
                    default:
                }
            }
            return authRequest;
        }

        private boolean isAllFieldsNull (Object object){
            if (object == null) {
                return true; // 对象本身为空
            }
            // 获取所有字段并判断是否所有字段都为空
            return Arrays.stream(ReflectUtil.getFields(object.getClass()))
                    // 获取字段值
                    .map(field -> ReflectUtil.getFieldValue(object, field))
                    // 检查是否所有字段都为空
                    .allMatch(ObjectUtil::isEmpty);
        }

        private List<String> getSpacePermissionsBySpaceRole (String spaceRole){
            List<SpaceRolePermDO> spaceRolePermDOList = spaceRolePermService.lambdaQuery()
                    .eq(SpaceRolePermDO::getRoleKey, spaceRole)
                    .list();
            return spaceRolePermDOList.stream()
                    .map(SpaceRolePermDO::getPermKey)
                    .collect(Collectors.toList());
        }


    }